//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using JetBrains.Annotations;
using LargoCommon.Abstract;
using LargoCommon.Localization;
using LargoCommon.Midi;
namespace LargoCommon.Music
{
///
/// Musical File.
///
[Serializable]
public sealed class MusicalBundle
{
#region Fields
///
/// Name of the model.
///
private string fileName;
///
/// Musical Blocks.
///
private Collection blocks;
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
public MusicalBundle() {
this.blocks = new Collection();
this.FileHeading = new FileHeading();
}
///
/// Initializes a new instance of the class.
///
/// The marked file.
public MusicalBundle(XElement xbundle)
: this() {
Contract.Requires(xbundle != null);
if (xbundle == null) {
return;
}
this.FileName = (string)xbundle.Attribute("FileName");
XElement xheading = xbundle.Element("Heading");
this.FileHeading = new FileHeading(xheading);
XElement xblocks = xbundle.Element("Blocks");
if (xblocks == null) {
return;
}
//// Blocks
foreach (var xblock in xblocks.Elements()) {
MusicalBlock block = new MusicalBlock(xblock) {
MusicalBundle = this
//// FileName = this.Name
};
this.Blocks.Add(block);
}
}
#endregion
///
/// Gets or sets the maximum number of bars in block.
///
///
/// The maximum number of bars in block.
///
public static int MaxNumberOfBarsInBlock { get; set; } //// CA1044 (FxCop)
#region Properties - Xml
/// Gets Xml representation.
/// Property description.
public XElement GetXElement {
get {
XElement xbundle = new XElement(
"MIF",
new XAttribute("FileName", this.FileName));
XElement xcopyright = new XElement(
"Software",
new XAttribute("Name", MusicalSettings.ApplicationName),
new XAttribute("Www", MusicalSettings.ApplicationWeb));
xbundle.Add(xcopyright);
var xheading = this.FileHeading.GetXElement;
xbundle.Add(xheading);
//// Blocks
XElement xblocks = new XElement("Blocks");
foreach (MusicalBlock block in this.Blocks.Where(block => block != null)) {
var xblock = block.GetXElement;
xblocks.Add(xblock);
}
xbundle.Add(xblocks);
return xbundle;
}
}
#endregion
#region Public Properties
///
/// Gets or sets the original path.
///
///
/// The original path.
///
public string OriginalPath { get; set; }
///
/// Gets the blocks.
///
///
/// The blocks.
///
public Collection Blocks {
get {
Contract.Ensures(Contract.Result>() != null);
return this.blocks;
}
}
///
/// Gets or sets the type of the model.
///
///
/// The type of the model.
///
public MusicalOriginType OriginType { get; set; } //// CA1044 (FxCop)
///
/// Gets or sets the file heading.
///
///
/// The file heading.
///
public FileHeading FileHeading { get; set; }
#endregion
#region Public Properties - Naming
/// Gets or sets file name.
/// Property description.
/// Returns value.
public string FileName {
get {
Contract.Ensures(Contract.Result() != null);
if (this.fileName == null) {
throw new InvalidOperationException("File name is null.");
}
return this.fileName;
}
set {
if (value == null) {
this.fileName = string.Empty;
//// throw new ArgumentException("Name cannot be set null.", "value");
return;
}
this.fileName = value;
}
}
///
/// Gets the actual name.
///
/// Property description.
[UsedImplicitly]
public string ActualName => string.Format(
CultureInfo.CurrentCulture,
"{0}_{1}{2}",
this.NameAndOrigin,
SupportCommon.DateTimeIdentifier,
this.OrchestrationName.ClearSpecialChars() ?? string.Empty).Trim();
/// Gets or sets ComposerId.
/// Property description.
/// Returns value.
public int? ComposerId { get; set; } //// CA1044 (FxCop)
#endregion
#region Public properties
///
/// Gets Musical Identification.
///
/// Property description.
[UsedImplicitly]
public IList Identification {
get {
var items = new List();
var item = new KeyValuePair("FileName", this.FileName);
items.Add(item);
//// items.AddRange(this.FileHeading.Identification);
return items;
}
}
///
/// Gets the identification string.
///
///
/// The identification string.
///
[UsedImplicitly]
public string IdentificationString {
get {
StringBuilder sb = new StringBuilder();
var idents = this.Identification;
foreach (var ident in idents) {
sb.AppendFormat("{0}: {1}\n", ident.Key, ident.Value);
}
foreach (var block in this.blocks) {
sb.AppendLine(string.Empty);
sb.AppendFormat("{0}/ {1}\n", block.Header.Number, block.Header.Name);
sb.Append(block.IdentificationString);
}
return sb.ToString();
}
}
///
/// Gets or sets a value indicating whether Is Selected.
///
/// Property description.
[UsedImplicitly]
public bool IsSelected { get; set; }
#endregion
#region Private properties
///
/// Gets the name and origin.
///
/// Property description.
private string NameAndOrigin {
get {
var origin = this.OriginType != MusicalOriginType.None ? this.OriginType.ToString() : string.Empty;
var clearName = string.Format(CultureInfo.CurrentCulture, "{0}{1}", this.FileName.ClearSpecialChars(), origin);
return clearName;
}
}
///
/// Gets or sets the name of the orchestration.
///
///
/// The name of the orchestration.
///
// ReSharper disable once UnusedAutoPropertyAccessor.Local
private string OrchestrationName { get; set; }
#endregion
#region Static factory methods
///
/// Gets the new musical file.
///
/// The file name.
/// Returns value.
public static MusicalBundle GetNewMusicalBundle(string fileName) {
var musicBundle = new MusicalBundle {
FileName = fileName,
blocks = new Collection(),
FileHeading = { WorkTitle = fileName }
};
return musicBundle;
}
///
/// Gets the new musical file.
///
/// The musical block.
/// The file name.
///
/// Returns value.
///
public static MusicalBundle GetEnvelopeOfBlock(MusicalBlock musicalBlock, string givenFileName) {
var musicBundle = new MusicalBundle {
FileName = givenFileName,
blocks = new Collection(),
FileHeading = { WorkTitle = givenFileName }
};
if (musicalBlock != null) {
musicBundle.Blocks.Add(musicalBlock);
musicalBlock.MusicalBundle = musicBundle;
}
return musicBundle;
}
///
/// Load MIDI File.
///
/// The file path.
/// Name of the internal.
/// If set to true [split multi-voice tracks].
///
/// Returns value.
///
public static MusicalBundle ReadMidiFile(string filePath, string internalName, FileSplit splitMultivoiceTracks) {
Contract.Requires(filePath != null);
if (string.IsNullOrEmpty(filePath)) {
return null;
}
ProcessLogger.Singleton.SendLogEvent(Path.GetFileName(filePath), LocalizedMusic.String("Reading Midi file ... "), 0);
var midiFile = new MidiFile(filePath);
var sequence = midiFile.Sequence;
sequence.InternalName = internalName;
return GetMusicalBundle(sequence, splitMultivoiceTracks);
}
///
/// Musicals the file.
///
/// The sequence.
/// The split multi-voice tracks.
///
/// Returns value.
///
public static MusicalBundle GetMusicalBundle(CompactMidiStrip givenSequence, FileSplit splitMultivoiceTracks) {
Contract.Requires(givenSequence != null);
const byte harmonicOrder = DefaultValue.HarmonicOrder;
//// Track can be saved only after back writing of events
var musicalBundle = GetNewMusicalBundle(givenSequence.InternalName);
musicalBundle.OriginType = MusicalOriginType.Original;
if (givenSequence.Format == 0) {
givenSequence = givenSequence.SplitTracksByInstruments();
givenSequence.SetTrackInstrumentsFromFirstOccurrence();
}
var midiBlocks = givenSequence.GetMidiBlocks(MaxNumberOfBarsInBlock);
foreach (var midiBlock in midiBlocks) {
var rhythmicOrder = MusicalProperties.RhythmicOrder(midiBlock.Header.Division, midiBlock.Header.Metric.MetricBeat, midiBlock.Header.Metric.MetricGround);
var block = MusicalBlock.NewMusicalBlock((MidiBlock)midiBlock, harmonicOrder, rhythmicOrder); /* sequence */
//// block.FileName = musicalBundle.Name;
block.TonalityKey = midiBlock.TonalityKey;
block.TonalityGenus = midiBlock.TonalityGenus;
if (block.Header.NumberOfMelodicLines > 0) {
MusicalLinearizer linearizer = new MusicalLinearizer(block.Header) { Lines = block.Strip.Lines };
linearizer.SplitTracksToParts(block, splitMultivoiceTracks);
linearizer.TransferPartsToTracks(true);
block.Strip.SetLines(linearizer.Lines);
block.Strip.RebuildChannels();
block.LoadFirstStatusToLines(); //// 2019/10
}
//// Skip block having only negligible tracks!? (2019/03)
if (!block.Strip.Lines.Any()) {
continue;
}
block.ConvertStripToBody(false);
var toneTracks = (from mt in ((MidiBlock)midiBlock).Sequence where mt.Sequence != null select mt)
.ToList();
block.Body.SetTempoEventsFrom(midiBlock.MidiTimeFrom, midiBlock.MidiTimeTo, toneTracks);
//// Status (lists of bars in tracks) will be transformed to blockStatus.
//// List of bar status in each given line have to be converted to Status in elements.
//// Read from midi file needs status from tones...!?
block.Body.SetBodyStatusFromTones();
//// MusicMidiWriter writer = new MusicMidiWriter(block);
//// CompactMidiStrip midiTracks = writer.WriteToSequence(false); //// staff grouping
//// block.SplitTracksToParts(splitMultivoiceTracks);
//// block.TransferPartsToTracks();
//// MusicMidiWriter.SaveBlockMidiToDatabase(block);
block.MusicalBundle = musicalBundle;
musicalBundle.Blocks.Add(block);
}
//// 2019/01 musicalBundle.SaveToMidi(false, splitMultivoiceTracks);
//// MusicMidiWriter.SaveMidiToDatabase(file, originalName + " (i-split)");
musicalBundle.FileName = givenSequence.InternalName;
return musicalBundle;
}
#endregion
#region Public methods
/// Makes a deep copy of the MusicalFile object.
/// Returns object.
[UsedImplicitly]
public object Clone() {
var file = GetNewMusicalBundle(this.FileName);
file.ComposerId = this.ComposerId;
file.FileHeading.Encoder = this.FileHeading.Encoder;
file.FileHeading.EncodingDate = this.FileHeading.EncodingDate;
file.FileHeading.EncodingDescription = this.FileHeading.EncodingDescription;
//// file.Identification = this.Identification;
file.FileHeading.Software = this.FileHeading.Software;
file.FileHeading.WorkNumber = this.FileHeading.WorkNumber;
file.FileHeading.WorkTitle = this.FileHeading.WorkTitle;
//// file.Blocks = new Collection();
foreach (var newBlock in this.Blocks.Select(block => block.Clone(true, true))) {
file.Blocks.Add(newBlock);
newBlock.MusicalBundle = file;
}
return file;
}
///
/// Sorts the blocks.
///
[UsedImplicitly]
public void SortBlocks() {
var bs = (from fb in this.Blocks orderby fb.Header.Number select fb).ToList();
this.blocks = new Collection(bs);
}
///
/// Saves the midi of musical file.
///
/// If set to true [check tracks].
/// The split multi-voice tracks.
public void SaveToMidi(bool checkTracks, FileSplit splitMultivoiceTracks) {
foreach (var block in this.Blocks.Where(block => block.Strip.Lines.Any())) {
if (checkTracks) {
//// this primarily splits percussion track according to instruments
//// bool split = splitMultivoiceTracks == FileSplit.Total || (splitMultivoiceTracks == FileSplit.Automatic && block.Strip.Lines.Count < 3);
MusicalLinearizer linearizer = new MusicalLinearizer(block.Header) { Lines = block.Strip.Lines };
linearizer.SplitTracksToParts(block, splitMultivoiceTracks);
linearizer.TransferPartsToTracks(true);
block.Strip.SetLines(linearizer.Lines);
block.Strip.RebuildChannels();
}
////201508 SaveMidiOfMusicalBlock(block, MidiSourceType.ReadBlock);
}
}
#endregion
}
}